package pers.mihao.quickstream.common.plus.wrapper;

import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import pers.mihao.quickstream.common.util.scan.CacheClassPathClassFileScanner;
import pers.mihao.quickstream.common.util.scan.ClassPathClassFileScanner;
import pers.mihao.quickstream.common.util.scan.filter.AnnotationScanFilter;

import java.lang.reflect.Constructor;
import java.util.*;

import static pers.mihao.quickstream.common.util.ObjectUtil.getInterface;

/**
 * @author mihao
 * @since 2022/7/19
 */
public class ComponentWrapperScan implements ImportBeanDefinitionRegistrar, BeanFactoryPostProcessor, EnvironmentAware, ResourceLoaderAware {

    private String[] wrapperScans;
    private Environment environment;
    private ResourceLoader resourceLoader;
    private Map<Class<?>, TreeSet<WrapperCreateInfo>> wrapperClassMap;
    private BeanDefinitionRegistry registry;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        if (wrapperClassMap == null) {
            wrapperClassMap = new HashMap<>(16);
        }
        // 扫描包下有ComponentWrapper注解的类
        ClassPathClassFileScanner scanner = new CacheClassPathClassFileScanner(environment);
        scanner.addIncludeFilter(new AnnotationScanFilter(ComponentWrapper.class));
        Set<Class<?>> wrapperClass = scanner.scanClass(wrapperScans);
        for (Class<?> clazz : wrapperClass) {
            parseWrapperClass(clazz);
        }
        // 注册bpp
        if (!wrapperClassMap.isEmpty()) {
            registerBeanWrapperCreatePostProcessor();
        }
    }

    private void registerBeanWrapperCreatePostProcessor() {
        RootBeanDefinition beanDefinition = new RootBeanDefinition(BeanWrapperCreatePostProcessor.class);
        MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
        mutablePropertyValues.addPropertyValue("wrapperClassMap", wrapperClassMap);
        beanDefinition.setPropertyValues(mutablePropertyValues);
        registry.registerBeanDefinition("beanWrapperCreatePostProcessor", beanDefinition);
    }


    private void parseWrapperClass(Class<?> clazz) {
        List<Class<?>> interfaceNames;
        int order = clazz.getAnnotation(ComponentWrapper.class).order();
        interfaceNames = getInterface(clazz);
        Constructor<?>[] constructors = clazz.getConstructors();
        for (Constructor<?> constructor : constructors) {
            if (constructor.getParameterTypes().length == 1 && interfaceNames.contains(constructor.getParameterTypes()[0])) {
                TreeSet<WrapperCreateInfo> set = wrapperClassMap.computeIfAbsent(constructor.getParameterTypes()[0],
                        k -> new TreeSet<>());
                set.add(new WrapperCreateInfo(clazz, constructor, order));
            }
        }
    }


    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
        AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(annotationMetadata.getAnnotationAttributes(EnableComponentWrapper.class.getName()));
        if (annoAttrs == null) {
            return;
        }
        String[] values = annoAttrs.getStringArray("wrapperScans");
        RootBeanDefinition beanDefinition = new RootBeanDefinition(ComponentWrapperScan.class);
        MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
        mutablePropertyValues.addPropertyValue("wrapperScans", values);
        mutablePropertyValues.addPropertyValue("registry", registry);
        beanDefinition.setPropertyValues(mutablePropertyValues);
        registry.registerBeanDefinition("componentWrapperScan", beanDefinition);
    }

    public void setWrapperScans(String[] wrapperScans) {
        this.wrapperScans = wrapperScans;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    public void setRegistry(BeanDefinitionRegistry registry) {
        this.registry = registry;
    }
}
